#pragma once

#include <shlobj.h>
#include <psapi.h>

#include "qcom.h" //for ALT+ENTER
using namespace micro::com;
using namespace micro::utils;


#include "qpidl.h"




namespace micro{
namespace shell{




class QShell
{
public:	

	static bool			IsFileExist(const QString& sPathName);
	static bool			IsPathExists(const QString& sPathName);
	static bool			IsRemovable(const QString& sPathName);
	
		
	static bool			StartFile(const QString &path,   const  QString & params = QString() );
	static bool			StartFile(const QPidl   &path,   const  QString & params = QString() );
	
	static bool			StartEditFile(const QString &path,  const  QString & params = QString() );


	static bool			GetDiskFreeSpace(const QString & path, QString &free, QString & all);
	static HICON		GetIcon(int index, UINT flags =0);
	
	
	//static QString		GetFileType( QString &strFileName);
	
	static QString		GetSizeFile(size_t filesize);
	static QString		GetUserName();


 
	///used by imagelist
	static HIMAGELIST   GetSystemImageListHandle( bool bSmallIcon = true);
	static HBITMAP		GetImage(const QString & path, int cx, int cy);
	static HBITMAP		GetImage(const QPidl & path, int cx, int cy);
	static void			AddToRecent(const QPidl & path, LPCTSTR appid);


	static int			GetFileIconIndex(LPCTSTR str);
	static int			GetAttrIconIndex(LPCTSTR str, UINT attributes = 0);
 
	static int			GetWinIconIndex(SHSTOCKICONID index);
	 
	static int			GetLinkIconIndex();



	static HRESULT		CreateShellContextMenu(HWND hwnd, QString parent, QStringArray & list, IContextMenu ** );
	static HRESULT		CreateShellContextMenu1(HWND hwnd, QString path,  IContextMenu  * * systemContextMenu);
	static HRESULT		CreateShellContextMenu2( const QPidl& pidl,  IContextMenu  * * systemContextMenu);
	static HRESULT      CreateShellContextMenu3( const QPidl&  pidl, const QPidlArray  &arr,  IContextMenu  * * systemContextMenu);

	
	static HRESULT		ShowContextMenu(const QPoint&  pt, IContextMenu * contextMenu, QString parent=L"");
	static HRESULT		ResolveLink(  const QString & source,  QString & target, bool & folder );
	

	
	static bool			CreateIFileOperation( IFileOperation **ppv,  bool undo = false);
	static bool			ShDeleteFile(const QString &path, bool recycle = true);
	 
	static bool			FileOpenDialog(QString &  file,  HWND hwnd);
	static bool			FileSaveDialog(QString &  file,  HWND hwnd);
	
	///same thread
	static HRESULT      CopyShellItemArray(IShellItemArray *psia, IShellItemArray **ppsiaOut);

	///inter single-apartments threads
	static HRESULT		StreamInterface(IShellItemArray *psia, IStream  **pstm);
	static HRESULT		UnStreamInterface(IStream  *pstm, IShellItemArray **ppsiaOut  );

	static HRESULT		ShowFilePicker( IShellItem **ppsi, HWND hwnd = nullptr );
	
	

	static void			ShowFileProperties(HWND hwnd, LPTSTR lpszFile, LPTSTR page = nullptr );
	static void			ShowMultiFileProperties(IDataObject* pdtobj);

	
	


	static QString		GetModulePath();
	static QString		GetModuleName();
	static QString		GetModuleDirectory();

 
};



// QPidl helpers
inline VOID SHChangeNotify(LONG wEventId, UINT uFlags, const QPidl& pidl)
{
   ASSERT((uFlags & SHCNF_TYPE) == SHCNF_IDLIST);
   ASSERT(!pidl.IsNull());
   ::SHChangeNotify(wEventId, uFlags, pidl.GetData(), nullptr);
}

inline VOID SHChangeNotify(LONG wEventId, UINT uFlags, const QPidl& pidl1, const QPidl& pidl2)
{
   ASSERT((uFlags & SHCNF_TYPE) == SHCNF_IDLIST);
   ASSERT(!pidl1.IsNull() && !pidl2.IsNull());
   ::SHChangeNotify(wEventId, uFlags, pidl1.GetData(), pidl2.GetData());
}

 
 

//HRESULT WalkFolderContents(HWND hwndTreeView, IShellItem *psiItem, HTREEITEM hTreeParent)
//{
//    INamespaceWalkCB *pnswcb = new (std::nothrow) QNameSpaceWalker(hwndTreeView, hTreeParent);
//    HRESULT hr = pnswcb ? S_OK : E_OUTOFMEMORY;
//    if (SUCCEEDED(hr))
//    {
//        INamespaceWalk *pnsw;
//        hr = CoCreateInstance(CLSID_NamespaceWalker, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&pnsw));
//        if (SUCCEEDED(hr))
//        {
//            hr = pnsw->Walk(psiItem, NSWF_ASYNC | NSWF_DONT_TRAVERSE_LINKS | NSWF_DONT_ACCUMULATE_RESULT, MAX_TREE_DEPTH, pnswcb);
//            pnsw->Release();
//        }
//        pnswcb->Release();
//    }
//    return hr;
//}


// INamespaceWalkCB implementation
class QNameSpaceWalker : public INamespaceWalkCB2
{


public:
    QNameSpaceWalker(HWND hwndTreeView, HTREEITEM hTreeParent): _cRef(1), _iCurTreeDepth(0), _hwndTreeView(hwndTreeView)
    {
        _rghTreeParentItemArray[_iCurTreeDepth] = hTreeParent;
    }

private:
    ~QNameSpaceWalker()
    { }

public:
    // IUnknown
    IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        static const QITAB qit[] =
        {
            QITABENT(QNameSpaceWalker, INamespaceWalkCB),
            QITABENT(QNameSpaceWalker, INamespaceWalkCB2),
            {0},
        };
        return QISearch(this, qit, riid, ppv);
    }

    IFACEMETHODIMP_(DWORD) AddRef()
    {
        return InterlockedIncrement(&_cRef);
    }

    IFACEMETHODIMP_(DWORD) Release()
    {
        ULONG cRef = InterlockedDecrement(&_cRef);
        if (0 == cRef)
        {
            delete this;
        }
        return cRef;
    }

    // INamespaceWalkCB
    IFACEMETHODIMP FoundItem(IShellFolder *psf, PCUITEMID_CHILD pidl)
    {
        IShellItem *psi;
        HRESULT hr = SHCreateItemWithParent(nullptr, psf, pidl, IID_PPV_ARGS(&psi));
        if (SUCCEEDED(hr))
        {
           /// hr = AddItemToTreeView(_hwndTreeView, psi, _rghTreeParentItemArray[_iCurTreeDepth]) ? S_OK : E_FAIL;
            psi->Release();
        }
        return hr;
    }

    IFACEMETHODIMP EnterFolder(IShellFolder *psf, PCUITEMID_CHILD pidl)
    {
        HRESULT hr = (_iCurTreeDepth < ARRAYSIZE(_rghTreeParentItemArray) - 1) ? S_OK : E_FAIL;
        if (SUCCEEDED(hr))
        {
            IShellItem *psi;
            hr = SHCreateItemWithParent(nullptr, psf, pidl, IID_PPV_ARGS(&psi));
            if (SUCCEEDED(hr))
            {
                ///_rghTreeParentItemArray[_iCurTreeDepth + 1] = AddItemToTreeView(_hwndTreeView, psi, _rghTreeParentItemArray[_iCurTreeDepth]);
                
				
				hr = _rghTreeParentItemArray[_iCurTreeDepth + 1] ? S_OK : E_FAIL;
                _iCurTreeDepth++;
                psi->Release();
            }
        }
        return hr;
    }

    IFACEMETHODIMP LeaveFolder(IShellFolder * /*psf*/, PCUITEMID_CHILD /*pidl*/)
    {
        HRESULT hr = _iCurTreeDepth > 0 ? S_OK : E_FAIL;
        if (SUCCEEDED(hr))
        {
            _rghTreeParentItemArray[_iCurTreeDepth--] = nullptr;
        }
        return hr;
    }


    IFACEMETHODIMP InitializeProgressDialog(PWSTR *ppszTitle, PWSTR *ppszCancel)
    { *ppszTitle = nullptr; *ppszCancel = nullptr; return E_NOTIMPL; }


    // INamespaceWalkCB2
    IFACEMETHODIMP WalkComplete(HRESULT /*hr*/)
    { return S_OK; }



    long _cRef;
    int _iCurTreeDepth;
    HWND _hwndTreeView;
    HTREEITEM _rghTreeParentItemArray[100];
};

 
 

//Global functions
BOOL GetFullFileName(LPCITEMIDLIST pidlFull, LPTSTR pszPathName);
BOOL GetDisplayName(LPCITEMIDLIST pidlFull, LPTSTR pszDisplayName);

 
 



//Wrapper class for an IContextMenu2 pointer
class CContextMenu2
{
friend class CContextMenu;
public:
	CContextMenu2();
	virtual ~CContextMenu2();

	void Attach(IContextMenu2* pIContextMenu2);
	virtual HRESULT HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
	virtual IContextMenu2* GetIContextMenu2() {return m_pIContextMenu2;}
	void Release();

private:
	IContextMenu2* m_pIContextMenu2;

};

//Wrapper class for an IContextMenu pointer
class CContextMenu
{
friend class CShellFolder;
public:
	CContextMenu();
	virtual ~CContextMenu();

	virtual HRESULT InvokeCommand(CMINVOKECOMMANDINFO& Ici);
	virtual HRESULT QueryInterface(REFIID iid, CContextMenu2& cm2);
	virtual HRESULT QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);

private:
	void Attach(IContextMenu* pContextMenu);
	IContextMenu* m_pIContextMenu;
};

//Wrapper class for a LPENUMIDLIST
class CEnumIDList
{
friend class CShellFolder;
public:
	CEnumIDList();
	virtual ~CEnumIDList();
	virtual HRESULT Next(ULONG Elements, QPidl& QPidl, ULONG& ulFetched);

private:
	void Attach(LPENUMIDLIST EnumList);
	LPENUMIDLIST m_pEnumIDList;
};


//Wrapper class for a LPSHELLFOLDER
class CShellFolder
{
public:
	CShellFolder();
	CShellFolder(const CShellFolder& csfSource);
	void operator=(const CShellFolder& csfSource);
	virtual ~CShellFolder();
	virtual void Delete();
	virtual HRESULT BindToObject(const QPidl& pidl, LPBC pbc, REFIID riid, CShellFolder& NewFolder);
	virtual HRESULT CompareIDs(LPARAM lParam, const QPidl& CPidl1, const QPidl& CPidl2);
	virtual void Copy(const CShellFolder& Source);
	virtual void Copy(LPSHELLFOLDER Source);
	virtual HRESULT CreateViewObject(HWND hwnd, REFIID riid, CContextMenu& ccm);
	virtual HRESULT EnumObjects(HWND hwndOwner, int grfFlags, CEnumIDList& cenumIDList);
	virtual HRESULT GetAttributesOf(UINT cidl, const QPidl& QPidl, ULONG& rgfInOut);
	virtual HRESULT SHGetDesktopFolder();
	virtual HRESULT GetUIObjectOf(HWND hwndOwner, UINT nItems, QPidl* CPidlArray, REFIID riid, UINT rgfReserved, CContextMenu& cm);
	virtual LPSHELLFOLDER GetIShellFolder() {return m_IShellFolder;}

private:
	HRESULT AddRef() {return m_IShellFolder->AddRef();}
	void Attach(LPSHELLFOLDER ShellFolder);

	LPSHELLFOLDER m_IShellFolder;
}; //class CShellFolder






 
}}  


 
 

